﻿#include <vf_pch.h>
#include <vf_laminate.h>
#include <VFAdapterAPIs.h>
#include <algorithm>
#include <map>
#include <vector>
#include <sqlite3.h>

namespace VFAdapter {

long ConvertColor(std::string clrStr) {
  static std::map<std::string, long> ClrMap{
      {"blue", RGB(0, 0, 255)},    {"green", RGB(0, 255, 0)},
      {"red", RGB(255, 0, 0)},     {"yellow", RGB(255, 255, 0)},
      {"brown", RGB(165, 42, 42)}, {"white", RGB(255, 255, 255)},
      {"cyan", RGB(0, 255, 255)},
  };

  std::transform(clrStr.begin(), clrStr.end(), clrStr.begin(), ::tolower);
  auto it = ClrMap.find(clrStr);
  if (it != ClrMap.end()) {
    return it->second;
  }
  if (clrStr.empty()) {
    return RGB(255, 0, 0);
  }

  BOOST_LOG_TRIVIAL(error) << "材料表颜色未映射,使用默认颜色(红色),请及时映射";
  return RGB(255, 0, 0);
};

std::string QueryMaterial(std::map<std::string, MaterialDesc> &materialMap,
                          sqlite3 *db, std::string const &materialName) {
  std::string errMsg;
  std::string sql =
      "SELECT "
      "Specification,Thickness,Units,Color_0_Degrees,Color_45_Degrees,Color_90_"
      "Degrees,Color_135_Degrees,Color_Other_Angles FROM FibMaterial WHERE "
      "Specification = '" +
      materialName + "';";

  auto QueryMaterialCallback = [](void *data, int argc, char **argv,
                                  char **azColName) -> int {
    MaterialDesc materialDesc;
    materialDesc.Specification = argv[0];
    materialDesc.Thickness = std::stod(argv[1]);
    std::string units = argv[2];
    std::transform(units.begin(), units.end(), units.begin(), ::toupper);
    if ("ENGLISH" == units) {
      materialDesc.Thickness *= 25.4;
    }
    materialDesc.Thickness *= MM_TO_COORD;

    materialDesc.Color0 = ConvertColor(argv[3]);
    materialDesc.Color45 = ConvertColor(argv[4]);
    materialDesc.Color90 = ConvertColor(argv[5]);
    materialDesc.Color135 = ConvertColor(argv[6]);
    materialDesc.ColorOther = ConvertColor(argv[7]);
    ((std::map<std::string, MaterialDesc> *)data)
        ->insert_or_assign(materialDesc.Specification, materialDesc);
    return 0;
  };

  char *pErrorMsg = nullptr;
  int rc = sqlite3_exec(db, sql.c_str(), QueryMaterialCallback, &materialMap,
                        &pErrorMsg);
  if (pErrorMsg != nullptr) {
    errMsg = pErrorMsg;
    sqlite3_free(pErrorMsg);
  }
  return errMsg;
}

//! 从数据库中查询得到Laminate信息
std::string QueryLaminate(LaminateDesc &laminateDesc, sqlite3 *db,
                          std::string const &laminateName) {
  std::string errMsg;
  std::string sql = "SELECT NetBoundary,ExtBoundary,LayupSurface,Origin FROM "
                    "FibLaminate WHERE Name = '" +
                    laminateName + "';";

  auto QueryLaminateCallback = [](void *data, int argc, char **argv,
                                  char **azColName) -> int {
    LaminateDesc *laminateDesc = (LaminateDesc *)data;
    laminateDesc->NetBoundary = std::stoll(argv[0]);
    laminateDesc->ExtBoundary = std::stoll(argv[1]);
    laminateDesc->LayupSurface = std::stoll(argv[2]);
    laminateDesc->Origin = std::stoll(argv[3]);
    return 0;
  };
  char *pErrorMsg = nullptr;
  int rc = sqlite3_exec(db, sql.c_str(), QueryLaminateCallback, &laminateDesc,
                        &pErrorMsg);
  if (pErrorMsg != nullptr) {
    errMsg = pErrorMsg;
    sqlite3_free(pErrorMsg);
  }

  return errMsg;
}

std::string QueryPly(std::vector<PlyDesc> &plyDescs, sqlite3 *db,
                     std::string const &laminateName) {
  std::string errMsg;
  std::string sql = "SELECT "
                    "Name,Material,Step,Sequence,NetBoundary,ExtBoundary,"
                    "SpecifiedOrientation FROM FibPly "
                    "WHERE Parent = '" +
                    laminateName + "';";
  auto QueryPlyCallback = [](void *data, int argc, char **argv,
                             char **azColName) -> int {
    PlyDesc plyDesc;
    plyDesc.Name = argv[0];
    plyDesc.Material = argv[1];
    plyDesc.Step = std::stoi(argv[2]);
    plyDesc.Sequence = argv[3];
    plyDesc.NetBoundary = std::stoll(argv[4]);
    plyDesc.ExtBoundary = std::stoll(argv[5]);
    plyDesc.SpecifiedOrientation = argv[6];
    ((std::vector<PlyDesc> *)data)->push_back(plyDesc);
    return 0;
  };
  char *pErrorMsg = nullptr;
  int rc = sqlite3_exec(db, sql.c_str(), QueryPlyCallback, &plyDescs, nullptr);
  if (pErrorMsg != nullptr) {
    errMsg = pErrorMsg;
    sqlite3_free(pErrorMsg);
  }
  return errMsg;
}

//!
//! @brief 对拓扑边取样以进行渲染
//!
//!

// HRESULT GetSamplePoints(std::vector<DPoint3d> &samplePoints,
//                         gm::EdgePtr const &pEdge, int sampleNum,
//                         DTransform3d const &transform) {

//   try {
//     samplePoints.clear();
//     // 参数区间等分，生成等分+1个点
//     double paramSt, paramEn;
//     pEdge->GetParamExtents(&paramSt, &paramEn);
//     double paraDelta = (paramEn - paramSt) / sampleNum;
//     ATL::CComSafeArray<double> params(sampleNum + 1);
//     for (int i = 1; i <= sampleNum; i++) {
//       params[i] = paramSt + i * paraDelta;
//     }
//     params[0] = paramSt;
//     params[sampleNum] = paramEn;

//     LPSAFEARRAY pPoints = nullptr;
//     pEdge->GetPointAtParam(params.GetCount(), params.GetSafeArrayPtr(),
//                            &pPoints);
//     samplePoints.resize(params.GetCount());

//     memcpy(samplePoints[0].data(), pPoints->pvData,
//            samplePoints.size() * 3 * sizeof(double));
//     SafeArrayDestroy(pPoints);
//     // 变换
//     for (auto &pt : samplePoints) {
//       pt = transform * pt;
//     }

//     return S_OK;
//   } catch (_com_error &e) {
//     return E_FAIL;
//   }
// }

vfHRESULT QueryLaminateRelateData(LaminateRelateData &data,
                                  std::string const &strLaminateName,
                                  const char *strDBPath) {

  sqlite3 *db = nullptr;
  int rc = sqlite3_open(strDBPath, &db);
  if (rc != SQLITE_OK) {
    return vf_Failure;
  }

  std::string errMsg = QueryLaminate(data.Desc, db, strLaminateName);
  if (!errMsg.empty()) {
    BOOST_LOG_TRIVIAL(error) << "查询铺层有误" << ",ErrMsg: " << errMsg;
    sqlite3_close(db);
    return vf_Failure;
  }

  errMsg = QueryPly(data.PlyList, db, strLaminateName);
  if (!errMsg.empty()) {
    BOOST_LOG_TRIVIAL(error) << "据铺层查层有误" << ",ErrMsg: " << errMsg;
    sqlite3_close(db);
    return vf_Failure;
  }

  for (auto const &plyDesc : data.PlyList) {
    if (!data.MaterialMap.contains(plyDesc.Material)) {
      errMsg = QueryMaterial(data.MaterialMap, db, plyDesc.Material);
      if (!errMsg.empty()) {
        BOOST_LOG_TRIVIAL(error) << "查询材质有误" << ",ErrMsg: " << errMsg;
        sqlite3_close(db);
        return vf_Failure;
      }
    }
  }

  sqlite3_close(db);
  return vf_Success;
}
} // namespace VFAdapter